﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using FDSimpleModelGenerator.Common;
using FDSimpleModelGenerator.Common.Mvc;
using FDSimpleModelGenerator.Common.Mvc.PageModels;
using FDSimpleModelGenerator.Entities;
using FDSimpleModelGenerator.Entities.CreateTables;
using FDSimpleModelGenerator.Entities.Enums;
using FDSimpleModelGenerator.Entities.Exceptions;
using FDSimpleModelGenerator.Entities.Histories;
using FDSimpleModelGenerator.IServices;
using FDSimpleModelGenerator.Models;
using FDSimpleModelGenerator.Models.ClassTemplates;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace FDSimpleModelGenerator.Pages
{
    /// <summary>
    /// 创建表
    /// </summary>
    [Authorize]
    public class CreateTableModel : BasicModel
    {
        private readonly IEnumerable<IGenerateTableService> _generateTableServices;
        private readonly IEnumerable<IDatabaseTableInfoService> _databaseTableInfoServices;
        private IGenerateTableService _generateTableService;
        private IDatabaseTableInfoService _databaseTableInfoService;
        private readonly IWebHostEnvironment _hostingEnvironment;
        private readonly string _saveFolderName = "gentableresults";
        private readonly IExceptionThrowFactory _exceptionThrowFactory;
        private readonly IUserGenericHistoryService _userGenericHistoryService;
        private readonly ICache _cache;
        private readonly IEnumerable<ILanguageSymbolHtml> _languageSymbolHtmls;
        private readonly DefaultAddColumnConfig _defaultAddColumnConfig;

        /// <summary>
        /// 模板列表
        /// </summary>
        public IList<NameValue<string>> Templates { get; private set; }

        /// <summary>
        /// 数据库类型
        /// </summary>
        public IList<NameValue<string>> DatabaseTypes { get; private set; }

        /// <summary>
        /// 数据库数据类型
        /// </summary>
        public IList<NameValue<string>> DbDataTypes { get; private set; }

        private readonly string DbTypeCacheKey = "dbType_create_cache_for_user";
        private readonly string UserDbTypeCacheKey = "user_dbType_create_cache_for_user"; // 补上用户id

        ///// <summary>
        ///// 数据库类型
        ///// </summary>
        //public DbType DbType { get; set; }

        public CreateTableModel(IEnumerable<IGenerateTableService> generateTableServices,
            IWebHostEnvironment hostingEnvironment,
            IExceptionThrowFactory exceptionThrowFactory, IUserGenericHistoryService userGenericHistoryService,
            IEnumerable<IDatabaseTableInfoService> databaseTableInfoServices, ICache cache,
            IEnumerable<ILanguageSymbolHtml> languageSymbolHtmls,
            IOptions<DefaultAddColumnConfig> deAddColOptions)
        {
            _languageSymbolHtmls = languageSymbolHtmls;
            var user = UserContext.Current.Context.User;
            DbTypeCacheKey = $"{DbTypeCacheKey}{user.Identity.Name}";
            UserDbTypeCacheKey = $"{UserDbTypeCacheKey}{user.Identity.Name}";
            _cache = cache;
            _exceptionThrowFactory = exceptionThrowFactory;
            _generateTableServices = generateTableServices;
            _databaseTableInfoServices = databaseTableInfoServices;
            _userGenericHistoryService = userGenericHistoryService;
            ChangeGenerateModelService();
            _hostingEnvironment = hostingEnvironment;
            _defaultAddColumnConfig = deAddColOptions.Value;
            Init();
        }

        /// <summary>
        /// 更改服务类型
        /// </summary>
        private void ChangeGenerateModelService()
        {
            var dbType = CacheManager.Default.Get<DbType>(DbTypeCacheKey);
            _generateTableService = _generateTableServices.FirstOrDefault(s => s.DbType == dbType);
            _databaseTableInfoService = _databaseTableInfoServices.FirstOrDefault(s => s.DbType == dbType);
            if (_databaseTableInfoService == null)
                _exceptionThrowFactory.Throw<ServiceConfigException>($"{nameof(IDatabaseTableInfoService)} was not register for {dbType}");
            if (_generateTableService == null)
                _exceptionThrowFactory.Throw<ServiceConfigException>($"{nameof(IGenerateTableService)} was not register for {dbType}");
        }

        private void Init()
        {
            var path = Path.Combine(_hostingEnvironment.WebRootPath, "Templates");
            Templates = Directory.GetFiles(path, "*.fdtpl").Select(t =>
                new NameValue<string> { Name = Path.GetFileNameWithoutExtension(t), Value = Path.GetFileName(t) }).ToList();

            var dbTypes = EnumUtils.GetEnumList<DbType, int>();
            DatabaseTypes = dbTypes.Select(p => new NameValue<string>
            {
                Name = p.Name,
                Value = p.Value.ToString()
            }).ToList();

            //DbDataTypes = _databaseTableInfoService.GetDbDataTypes(dbcon);
        }

        /// <summary>
        /// 显示页面
        /// </summary>
        public void OnGet()
        {
        }

        /// <summary>
        /// 默认配置
        /// </summary>
        /// <returns></returns>
        public IActionResult OnGetDefaultColumns()
        {
            return new JsonResult(ApiResult.Success(_defaultAddColumnConfig));
        }

        /// <summary>
        /// 设置连接信息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<IActionResult> OnPostSetConfig([FromBody] ConfigSettingModel model)
        {
            var dbConfig = new DbConfig
            {
                Database = model.Database,
                DbType = model.DbType,
                Name = model.DbType.ToString(),
                Port = model.Port.ToString(),
                Password = HttpUtility.UrlDecode(model.Password), //"DX.net123!@#",
                ServerName = model.ServerName, //"192.168.50.79",
                UserName = model.UserId
            };
            SaveDbTypeConfig(model.DbType, model);
            CacheManager.Default.Set(DbTypeCacheKey, model.DbType, TimeSpan.FromHours(1));
            ChangeGenerateModelService();
            var result = (await _databaseTableInfoService.GetDbDataTypesAsync(dbConfig))
                .Select(t => new SelectItem
                {
                    Id = t.Name,
                    Text = t.Name,
                    MaxLength = t.MaxLength
                });

            return new JsonResult(ApiResult.Success(result));
        }

        /// <summary>
        /// Post 生成模型
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> OnPostGenerate([FromBody] GenCreateTableModel model)
        {
            var dbConfig = new DbConfig
            {
                Database = model.Database,
                DbType = model.DbType,
                Name = model.DbType.ToString(),
                Port = model.Port.ToString(),
                Password = HttpUtility.UrlDecode(model.Password), //"DX.net123!@#",
                ServerName = model.ServerName, //"192.168.50.79",
                UserName = model.UserId
            };

            var result = await GenTable(dbConfig, model);
            return new JsonResult(ApiResult.Success(result));
        }

        /// <summary>
        /// 生成表内容
        /// </summary>
        /// <param name="dbConfig"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        private async Task<GenerateTableModel> GenTable(DbConfig dbConfig, GenCreateTableModel model)
        {
            var fileName = $"{model.TableName}.sql"; // 因为cs文件不能下载
            var pathSave = $"{_saveFolderName}/{UserName}";
            var savePath = Path.Combine(_hostingEnvironment.WebRootPath, pathSave, fileName);
            FileSystemUtil.CreateIfDirNotExists(Path.Combine(_hostingEnvironment.WebRootPath, pathSave));

            Func<string, int> toInt = str => { int.TryParse(str, out int val); return val; };
            // 分隔成长度,精度
            Func<string, int, int> toLength = (str, index) =>
            {
                var items = str.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                if (items.Length == 2)
                {
                    int.TryParse(items[index], out int v);
                    return v;
                }
                if (index > 0)
                {
                    return 0;
                }
                int.TryParse(str, out int val);
                return val;
            };
            var table = new Table
            {
                Name = model.TableName,
                Description = model.Description,
                ExecuteSql = model.ExecuteSql,
                DeleteExists = model.DeleteExists,
                Columns = model.Columns.Select(c => new Column
                {
                    Name = c.ColumnName.Trim(),
                    AllowNulls = c.AllowNulls,
                    Unsigned = c.Unsigned,
                    DataType = c.DataType.Trim(),
                    DefaultValue = c.DefaultValue.Trim(),
                    IncrementValue = toInt(c.IncrementValue),
                    Length = toLength(c.Length, 0),
                    Precision = toLength(c.Length, 1),
                    PrimaryKey = c.PrimaryKey,
                    Remark = c.Remark.Trim()
                }).ToList(),
                Indexes = model.Indexes.Select(i => new ColumnIndex
                {
                    IndexName1 = i.IndexName1,
                    IndexName2 = i.IndexName2,
                    IndexName3 = i.IndexName3,
                    IndexName4 = i.IndexName4,
                    IndexName5 = i.IndexName5,
                    UniqueIndex = i.UniqueIndex
                }).ToList()
            };
            var result = await _generateTableService.GenerateAsync(dbConfig, table);

            System.IO.File.WriteAllText(savePath, result.Result, Encoding.UTF8);

            var tmp = _languageSymbolHtmls.First(h => h.LanguageDataType == Common.Enums.LanguageDataType.Sql)
                .ReplaceClsType(result.Result);//new SqlSymbolHtml().ReplaceClsType(result.Result);

            await _userGenericHistoryService.AddAsync(new UserGenericHistory
            {
                CreateTime = DateTime.Now,
                FileName = $"{pathSave}/{fileName}",
                ShortFileName = fileName,
                GenericType = GenericType.Table,
                IsEnable = true,
                UserId = UserId,
                Ip = HttpContext.RequestIp() //Request.HttpContext.Connection.RemoteIpAddress.ToString()
            });
            return new GenerateTableModel
            {
                TableName = model.TableName,
                SourceContent = result.Result,
                HtmlContent = tmp,
                FileLink = fileName
            };
        }

        /// <summary>
        /// 获取保存的配置信息
        /// </summary>
        /// <param name="dbType"></param>
        /// <returns></returns>
        public IActionResult OnGetSettingConfig(int dbType)
        {
            var type = (DbType)dbType;
            var config = GetDbTypeConfig(type);
            return new JsonResult(config == null ? ApiResult.Failure(ApiResultCode.Failure, "不存在配置信息") : ApiResult.Success(config));
        }

        private void SaveDbTypeConfig(DbType dbType, ConfigSettingModel configSettingModel)
        {
            _cache.Set($"{UserDbTypeCacheKey}{dbType}", configSettingModel, TimeSpan.FromHours(1));
        }

        private ConfigSettingModel GetDbTypeConfig(DbType dbType)
        {
            return _cache.Get<ConfigSettingModel>($"{UserDbTypeCacheKey}{dbType}");
        }
    }
}
